package com.nineclock.attendance.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.attendance.dto.AttendGroupDTO;
import com.nineclock.attendance.dto.AttendPunchDTO;
import com.nineclock.attendance.dto.AttendPunchUserWholeDayDTO;
import com.nineclock.attendance.dto.AttendsDTO;
import com.nineclock.attendance.enums.AttendEnums;
import com.nineclock.attendance.mapper.AttendPunchMapper;
import com.nineclock.attendance.pojo.AttendPunch;
import com.nineclock.attendance.service.AttendGroupService;
import com.nineclock.attendance.service.AttendPunchService;
import com.nineclock.attendance.utile.PunchUtils;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.feign.SysOrganizationFeign;
import com.nineclock.system.pojo.SysCompanyUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AttendPunchServiceImpl implements AttendPunchService {

    @Autowired
    AttendPunchMapper attendPunchMapper;
    @Autowired
    AttendGroupService attendGroupService;
    @Autowired
    SysOrganizationFeign organizationFeign;

    /**
     * 打卡（封装打卡信息）
     * 1. 转换为打卡对象
     * 2. 获取用户考勤组
     * 3. 判断是否为有效打卡(是否在范围内， 是否为工作日， 是否为有效打卡（保存打卡类型）)
     * 4. 保存打卡信息
     *
     * @param attendPunchDTO
     */
    @Override
    public void punch(AttendPunchDTO attendPunchDTO) {
        if (attendPunchDTO == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        //获取考勤组对象
        AttendGroupDTO attendGroupDTO = attendGroupService.getAttendGroupByUserId();

        //把dto转换为pojo
        AttendPunch attendPunch = BeanHelper.copyProperties(attendPunchDTO, AttendPunch.class);

        //设置基础信息
        attendPunch.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendPunch.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());
        attendPunch.setLat(new BigDecimal(attendPunchDTO.getLat()));
        attendPunch.setLng(new BigDecimal(attendPunchDTO.getLng()));
        attendPunch.setPunchTime(new Date());
        attendPunch.setPunchDateStr(DateUtil.format(new Date(), "yyyy-MM-dd")); //打卡时间 日期（年月日）
        attendPunch.setCreateTime(new Date());
        //上、下午打卡
        attendPunch.setPunchOnOffWork(DateTimeUtil.noonType(new Date()) + 1);

        //是否工作日打卡
        boolean validateWorkDay = PunchUtils.validateWorkDay(attendGroupDTO);
        if (!validateWorkDay) {
            throw new NcException(ResponseEnum.PUNCH_INVALID_DAY);
        }


        //是否在有效范围打卡
        boolean flag = PunchUtils.validatePunchArea(attendPunchDTO, attendGroupDTO);
        if (!flag) {
            throw new NcException(ResponseEnum.PUNCH_INVALID_AREA);
        }
        attendPunch.setAreaValid(flag);

        //是否有效打卡
        boolean effectiveValid = this.validatePunch(attendPunch);
        attendPunch.setEffectiveValid(effectiveValid);
        if (effectiveValid) {
            //设置打卡类型
            PunchUtils.setPunchType(attendPunch, attendGroupDTO);
        }

        attendPunchMapper.insert(attendPunch);
    }

    @Override
    public AttendPunchUserWholeDayDTO queryPunchRecord() {
        AttendPunchUserWholeDayDTO punchUserWholeDayDTO = new AttendPunchUserWholeDayDTO();
        //上午有效打卡情况
        LambdaQueryWrapper<AttendPunch> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId());
        wrapper.eq(AttendPunch::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        wrapper.eq(AttendPunch::getEffectiveValid, true);
        wrapper.eq(AttendPunch::getPunchOnOffWork, AttendEnums.NOON_TYPE_MORNING.value());
        wrapper.eq(AttendPunch::getPunchDateStr, DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_2));
        AttendPunch attendPunch = attendPunchMapper.selectOne(wrapper);

        //下午有效打卡情况
        LambdaQueryWrapper<AttendPunch> pmQueryWrapper = new LambdaQueryWrapper<AttendPunch>();
        pmQueryWrapper.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId())
                .eq(AttendPunch::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId())
                .eq(AttendPunch::getEffectiveValid, true)
                .eq(AttendPunch::getPunchOnOffWork, AttendEnums.NOON_TYPE_AFTERNOON.value())
                .eq(AttendPunch::getPunchDateStr, DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_2));
        List<AttendPunch> attendPunches = attendPunchMapper.selectList(pmQueryWrapper);

        AttendPunch pmPunch = attendPunchMapper.selectOne(pmQueryWrapper);
        //3. 组装数据
        punchUserWholeDayDTO.setAttendPunchMorningResponse(BeanHelper.copyProperties(attendPunch, AttendPunchDTO.class));
        punchUserWholeDayDTO.setAttendPunchAfterNoonResponse(BeanHelper.copyProperties(pmPunch, AttendPunchDTO.class));

        return punchUserWholeDayDTO;
    }

    @Override
    public List<AttendPunchDTO> queryMembers(String startTime, String endTime) {
        // 获取该企业的所有员工        远程查询(): 姓名,工号,部门,职位
        List<SysCompanyUserDTO> companyUserDTOList = organizationFeign.queryAllCompanyUser().getData();


        //根据时间段查询打卡情况
        //获取指定时间每一天的时间列表
        List<Date> dates = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, "yyyy-MM-dd");
        //把日期转成 字符串
        List<String> collect = dates.stream().map(date -> {
            return DateTimeUtil.dateToStr(date, "yyyy-MM-dd");
        }).collect(Collectors.toList());

        //创建返回值对象
        List<AttendPunchDTO> attendPunchDTOS = new ArrayList<>();


        if (CollectionUtil.isNotEmpty(companyUserDTOList)){
            for (SysCompanyUserDTO sysCompanyUserDTO : companyUserDTOList) {

                AttendPunchDTO attendPunchDTO = BeanHelper.copyProperties(sysCompanyUserDTO, AttendPunchDTO.class);

                //查询员工时间段内所有考勤情况
                LambdaQueryWrapper<AttendPunch> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(AttendPunch::getCompanyId,sysCompanyUserDTO.getCompanyId());
                wrapper.eq(AttendPunch::getCompanyUserId,sysCompanyUserDTO.getId());
                wrapper.between(AttendPunch::getPunchDateStr,startTime,endTime);
                wrapper.eq(AttendPunch::getEffectiveValid,true);
                //所有考勤有效信息
                List<AttendPunch> attendPunches = attendPunchMapper.selectList(wrapper);
                //进行分组  map< 日期, 打卡状态 >
                Map<String, List<AttendPunch>> map = attendPunches.stream().collect(Collectors.groupingBy(AttendPunch::getPunchDateStr));


                List<AttendsDTO> attendsList = new ArrayList<>();
                //对日期循环
                for (String s : collect) {
                    AttendsDTO attendsDTO = new AttendsDTO();
                    attendsDTO.setAttendDate(s);
                    //获取某一天的打卡记录
                    List<AttendPunch> attendPunches1 = map.get(s);
                    attendsDTO.setAttendStatus(this.handleAttendStatus(attendPunches1));

                    attendsList.add(attendsDTO);
                }
                attendPunchDTO.setAttendsList(attendsList);
                attendPunchDTOS.add(attendPunchDTO);
            }
        }
        return attendPunchDTOS;
    }

    private String handleAttendStatus(List<AttendPunch> punches) {
        String amAttendStatus = "缺卡";//上午 - 考勤
        String pmAttendStatus = "缺卡";//下午 - 考勤
        if(CollectionUtil.isNotEmpty(punches)){
            for (AttendPunch punch : punches) {
                if(punch.getPunchOnOffWork() == AttendEnums.NOON_TYPE_MORNING.value()){
                    amAttendStatus = this.getPushDesc(punch.getMorningPunchType());
                }else if (punch.getPunchOnOffWork() == AttendEnums.NOON_TYPE_AFTERNOON.value()){
                    pmAttendStatus = this.getPushDesc(punch.getAfternoonPunchType());
                }
            }
        }
        return amAttendStatus+"/"+pmAttendStatus;
    }


    private String getPushDesc(Integer morningPunchType) {
        String desc = "";
        switch (morningPunchType){
            case 1:
                desc = AttendEnums.PUNCH_TYPE_OK.toString();
                break;
            case 2:
                desc = AttendEnums.PUNCH_TYPE_LATE.toString();
                break;
            case 3:
                desc = AttendEnums.PUNCH_TYPE_EARLY.toString();
                break;
            case 4:
                desc = AttendEnums.PUNCH_TYPE_STAYALAY.toString();
                break;
            case 5:
                desc = AttendEnums.PUNCH_TYPE_NOT_WORK_DAY.toString();
                break;
            default:
                break;
        }
        return desc;
    }


    /**
     * 1. 查询打卡记录，如果没有查询到，无论何时打卡都是有效的
     * 2. 查询到了
     * 如果是上午打卡， 则无效
     * 如果是下午打卡， 以最后一次打卡记录为准, 之前打卡记录无效
     *
     * @param attendPunch
     * @return
     */
    private boolean validatePunch(AttendPunch attendPunch) {
        LambdaQueryWrapper<AttendPunch> punchQw = new LambdaQueryWrapper<>();

        punchQw.eq(AttendPunch::getPunchOnOffWork, attendPunch.getPunchOnOffWork());
        punchQw.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId());
        punchQw.eq(AttendPunch::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        punchQw.eq(AttendPunch::getPunchDateStr, attendPunch.getPunchDateStr());

        Integer count = attendPunchMapper.selectCount(punchQw);

        if (count == 0) {
            attendPunch.setEffectiveValid(true);
        } else {
            if (attendPunch.getPunchOnOffWork() == 1) {
                attendPunch.setEffectiveValid(false);
            } else {
                attendPunch.setEffectiveValid(true);

                AttendPunch updateAttendPunch = new AttendPunch();
                updateAttendPunch.setEffectiveValid(false);
                attendPunchMapper.update(updateAttendPunch, punchQw);

            }


        }

        return attendPunch.getEffectiveValid();
    }
}


